import React, { Component } from 'react'
import { connect } from '@umijs/max'
import {
  notification, message, Spin, Modal, Upload, TreeSelect, Form,
} from 'antd'
import sqlFormatter from 'sql-formatter'
import list2tree from 'list2tree'
import CreateDiffEditor from './createDiffEditor'
import TaskParam from './taskParam'
import Guide from './indexGuide'
import Tools from '@/utils/Tools'
import { DataDevLogger, ConsanguinityModal } from '@/components'
import {
  taskLog, stopTask, lockTask, getScript, saveScript, startScript, getWorkTree, tableList,
} from './api'
import { getProjectId, SUAN_PAN } from '@/utils/https'
import styles from './index.less'

const Bus = Tools.Bus

function noop() { }

const monacoCache = {}
const getNavTreeDataFunc = list2tree({
  idKey: 'id',
  parentIdKey: 'pid',
  newKey: {
    key: 'id',
    value: 'id',
    title: 'name',
  },
})

const loadScript = (url, callback) => {
  const script = document.createElement('script')
  if (script.readyState) {
    // IE
    script.onreadystatechange = function () {
      if (script.readyState === 'loaded' || script.readyState === 'complete') {
        script.onreadystatechange = null
        callback()
      }
    }
  } else {
    // 其他浏览器
    script.onload = function () {
      callback()
    }
  }
  script.src = url
  document.getElementsByTagName('head')[0].append(script)
}

const save = (params, cb, failCb) => {
  saveScript(params).then(
    (res) => {
      cb && cb(res)
    },
    (error) => {
      failCb && failCb()
    },
  )
}

let db_schema = { db_keywords: [], table_columns: {}, tables: [] }
@connect(({ global }) => ({ ...global }))
export default class MonacoEditor extends Component {
  state = {
    isSave: true,
    show: false,
    spinning: false,
    spinTip: '',
    defaultValue: {
      13: `-- ********************************************************************--\n-- author: ${this.props.user.username
        // eslint-disable-next-line indent
        }\n-- create time: ${new Date()}\n-- ********************************************************************--\n`,
      14: `-- ********************************************************************--\n-- author: ${this.props.user.username
        // eslint-disable-next-line indent
        }\n-- create time: ${new Date()}\n-- ********************************************************************--\n`,
      15: `-- ********************************************************************--\n-- author: ${this.props.user.username
        // eslint-disable-next-line indent
        }\n-- create time: ${new Date()}\n-- ********************************************************************--\n`,
      16: '#!/bin/bash\n',
      17: '#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n',
      18: '#!/usr/bin/python3\n',
      19: '#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n',
    },
    language: {
      16: 'shell',
      17: 'python',
      18: 'python',
      19: 'python',
      13: 'sql',
      14: 'sql',
      15: 'sql',
    },
    isLocked: this.props.editorTaskItem.isLocked ? 1 : 0,
    jobId: 0,
    log_offset: 0,
    loggerList: [],
    rsHasShow: [],
    hide: true,
    writeAuth: true,
    runState: 0,
    resultss: [
      {
        result: '运行日志',
        value: ['暂无运行'],
        key: 'logger',
        name: '运行日志',
      },
    ],
    activeKey: '0',
    isRunState: false,
    script: '',
    showGuide: false,
    workModalVisible: false,
    workTreeOptions: [],
  };

  editor = null;

  runTime = null;

  logTimeout = null;

  checkLogTimeout = null;

  // child = null;

  timer = null;

  time = null;

  timeClear = null;

  tid = null;

  saveTime = null;

  workForm = React.createRef()

  // db_schema = {}

  componentDidMount() {
    this.initMonaco()
    Bus.addListener('insertQuerySql', this.insertQuerySql)
    Bus.addListener('editorLayout', this.layoutByBus)
    Bus.addListener('datadevEditorSave', this.saveEditor)
    if (this.props.showImportWork) {
      this.getWorkTreeOptions()
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (!(this.props.bool === nextProps.bool)) {
      this.time = setInterval(this.layout)
      this.timeClear = setTimeout(() => {
        clearInterval(this.time)
      }, 600)
    }
    return true
  }

  componentWillUnmount() {
    delete monacoCache[this.props.tabKey]
    this.editor && this.editor.dispose()
    this.runTime && clearInterval(this.runTime)
    this.logTimeout && clearTimeout(this.logTimeout)
    this.checkLogTimeout && clearTimeout(this.checkLogTimeout)
    this.timer && clearTimeout(this.timer)
    this.timeClear && clearTimeout(this.timeClear)
    this.time && clearInterval(this.time)
    this.saveTime && clearTimeout(this.saveTime)
    // 保存后清除重复的bus事件
    Bus.removeListener('datadevEditorSave', this.saveEditor)
    Bus.removeListener('insertQuerySql', this.insertQuerySql)
    Bus.removeListener('editorLayout', this.layoutByBus)
    Bus.removeListener('refreshTableInfo', this.refreshTableInfo)
    window.removeEventListener('resize', this.layout)
    this.setState = (state, callback) => {

    }
  }

  // eslint-disable-next-line react/sort-comp
  layoutByBus = (id) => {
    if (this.props.editorTaskItem.id === id) {
      this.layout()
    }
  }

  // eslint-disable-next-line react/sort-comp
  layout = () => {
    clearTimeout(this.tid) // 防止执行两次
    this.tid = setTimeout(() => {
      this.editor && this.editor.layout()
    }, 400)
  }

  onEdit = (key) => {
    const { resultss } = this.state
    const results = [...resultss]
    results.splice(key, 1)
    results.map((item, k) => {
      item.name === '运行日志' ? null : (item.name = `结果[${k}]`)
    })

    this.setState({
      resultss: results,
      activeKey: String(results.length - 1),
    })
  };

  resultssChange = (resultss, activeKey) => {
    this.setState({
      resultss,
      activeKey,
    })
  };

  activeKeyonChange = (activeKey) => {
    this.setState({ activeKey })
  };

  onRef(ref) {
    // eslint-disable-next-line react/no-unused-class-component-methods
    this.child = ref
  }

  refreshTableInfo = async () => {
    tableList().then((data) => {
      db_schema = data
    })
  };

  // 获取作业选项
  getWorkTreeOptions = (cb) => {
    getWorkTree().then(
      (res) => {
        const newArr = res.map((item) => {
          if (item.nodeType !== 13) {
            item.disabled = true
          }
          return item
        })
        this.setState({
          workTreeOptions: newArr,
        }, () => {
          cb && cb()
        })
      },
      (error) => {
      },
    )
  }

  // 引用作业确认回调
  onImportWorKOk = () => {
    this.workForm.current.validateFields().then(values => {
      getScript({ id: values.workId }).then(
        (res) => {
          message.success('引用成功')
          this.insertToCursor(res.script)
          this.setState({
            workModalVisible: false,
          })
        },
        (error) => {
          notification.warning({
            message: '引用失败.',
            style: {
              paddingRight: 20,
            },
          })
        },
      )
    })
  }

  createMonacoInstance = () => {
    const { editorTaskItem, themeValue } = this.props
    // 自定义编辑器颜色
    window.monaco.editor.defineTheme('BlackTheme', themeValue === 'dark' ? {
      base: 'vs-dark',
      inherit: true,
      rules: [{ fontSize: 14, editorBackground: '#020202' }],
      colors: {
        'editorGutter.background': '#020202',
        'editor.background': '#020202',
      },
    } : {
      base: 'vs',
      inherit: true,
      rules: [{ background: '#F2F3F5' }],
      colors: {
        // 相关颜色属性配置
        // 'editor.foreground': '#000000',
        'editor.background': '#F2F3F5', // 背景色
        // 'editorCursor.foreground': '#8B0000',
        // 'editor.lineHighlightBackground': '#0000FF20',
        // 'editorLineNumber.foreground': '#008800',
        // 'editor.selectionBackground': '#88000030',
        // 'editor.inactiveSelectionBackground': '#88000015'
      },
    })
    // 创建编辑器
    this.editor = window.monaco.editor.create(this.monacoEditor, {
      language: this.state.language[editorTaskItem.nodeType],
      theme: 'BlackTheme',
      suggestOnTriggerCharacters: true,
    })

    this.editor.addAction({
      id: 'custom-run',
      label: '运行',
      keybindings: [window.monaco.KeyCode.F8],
      precondition: null,
      keybindingContext: null,
      contextMenuGroupId: 'navigation',
      contextMenuOrder: 0,
      run: () => {
        this.run()
        return null
      },
    })

    this.editor.addAction({
      id: 'custom-formatter',
      label: '格式化文档',
      // keybindings: [monaco.KeyCode.F8],
      precondition: null,
      keybindingContext: null,
      contextMenuGroupId: 'navigation',
      contextMenuOrder: 1,
      run: (editor) => {
        const value = sqlFormatter.format(editor.getValue())
        this.executeEdits(editor, value)
        return null
      },
    })

    // eslint-disable-next-line no-bitwise
    this.editor.addCommand(window.monaco.KeyMod.CtrlCmd | window.monaco.KeyCode.KEY_S, () => {
      const { isSave } = this.state
      if (isSave) {
        this.saveTime && clearTimeout(this.saveTime)
        this.save()
      }
    })
    this.editor.addCommand(window.monaco.KeyCode.F8, this.run)
    this.editor.addCommand(window.monaco.KeyCode.F9, this.stop)

    // 监听变化
    this.editor.onDidChangeModelContent((event) => {
      const arr = []
      const value = this.editor.getValue()
      clearTimeout(this.timer)
      if (!this.state.isLocked) {
        this.timer = setTimeout(() => {
          this.setState({
            script: value,
          })
          const reg = /\$\{(\w+)\}/g
          const data = value.match(reg)
          const newArr = data && data.length && data.map((item) => item.replace(reg, '$1'))
          const regVal = Array.from(new Set(newArr))
          regVal
            && regVal.map((val) => {
              this.independentValidity(val)
                ? arr.push({
                  paramName: val,
                  paramValue: '',
                })
                : null
            })
          if (arr) {
            const taskItem = this.props.editorTaskItem
            this.props.curstoMvariablesTask && this.props.curstoMvariablesTask(taskItem, arr || [])
          }

          // 判断是否编辑
          if (!monacoCache[this.props.tabKey]) {
            monacoCache[this.props.tabKey] = value
          } else {
            (this.props.editorContentChange || noop)(
              this.props.tabKey,
              monacoCache[this.props.tabKey] !== value,
            )
          }

          if (sessionStorage.getItem('dataDevArr') && !(sessionStorage.getItem('dataDevArr') === 'undefined') && !(sessionStorage.getItem('dataDevArr') === 'null')) {
            const arr1 = JSON.parse(sessionStorage.getItem('dataDevArr'))
            const newArr1 = []
            arr1.map((item) => {
              item.id === this.props.tabKey
                ? newArr1.push({ id: this.props.tabKey, value })
                : newArr1.push(item)
            })
            sessionStorage.setItem('dataDevArr', JSON.stringify(newArr1))
          }
          // 错误提示
          // let model = monaco.editor.getModels()[0];
          // monaco.editor.setModelMarkers(model, "owner", [
          // {
          //   startLineNumber: 5,
          //   startColumn: 1,
          //   endLineNumber: 5,
          //   endColumn: 4,
          //   message: "语法错误", // 提示文案
          //   severity: monaco.MarkerSeverity.Error, // 提示的类型
          // },
          // ]);
          (this.props.onChange || noop)(value, this.props.key, event)
        }, 1000)
      } else if (this.state.script !== value) {
        this.executeEdits(this.editor, this.state.script)
        this.setState({
          showGuide: true,
        })
      }
      this.saveTime && clearTimeout(this.saveTime)
      this.setTimeoutFn()
    })
    window.addEventListener('resize', this.layout)
    this.fetchDetail() // 请求服务端spl代码和本地存储spl代码
  };

  // 编辑器初始化
  initMonaco = () => {
    const baseUrl = `${window.rootPath || window.location.origin}${window.location.pathname}monaco-editor/min/`
    !window.monaco
      ? loadScript(`${baseUrl}vs/loader.js`, () => {
        const req = window.require
        req.config({ paths: { vs: `${baseUrl}vs` } })

        req.config({
          'vs/nls': {
            availableLanguages: {
              '*': 'zh-cn',
            },
          },
        })
        window.MonacoEnvironment = {
          getWorkerUrl(workerId, label) {
            return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
            self.MonacoEnvironment = {
              baseUrl: '${baseUrl}'
            };
            importScripts('${baseUrl}vs/base/worker/workerMain.js');`)}`
          },
        }
        req(['vs/editor/editor.main'], () => {
          this.configSuggestion()
          this.createMonacoInstance()
        })
      })
      : this.createMonacoInstance()
  };

  // 运行任务
  run = async () => {
    const run = (params, cb, failCb) => {
      startScript(params).then(
        (res) => {
          const { status } = res
          if (Number(status) === 200) {
            cb && cb(res)
          } else {
            failCb && failCb(res)
          }
        },
        (error) => {
          console.log(error)
        },
      )
    }
    let valArr = null
    let bool = true
    if (this.props.curstoMvariablesArr) {
      this.props.curstoMvariablesArr.map((obj) => {
        if (this.props.tabKey === (obj.task && obj.task.id)) {
          valArr = obj.arr
        }
      })
    }
    if (this.editor.getSelection().isEmpty()) {
      valArr
        && valArr.map((obj) => {
          if (!obj.paramValue) {
            bool = false
          }
        })
    } else {
      const name = this.editor.getModel().getValueInRange(this.editor.getSelection())
      const reg = /\$\{(\w+)\}/g
      const data = name.match(reg)
      const newArr = data && data.length && data.map((item) => item.replace(reg, '$1'))
      const regVal = Array.from(new Set(newArr))
      valArr
        && valArr.map((obj) => {
          if (regVal.indexOf(obj.paramName) !== -1 && !obj.paramValue) {
            bool = false
          }
        })
    }

    if (bool) {
      Bus.emit('iconUpFn', this.props.editorTaskItem.id)
      if (this.state.runState === 1) return
      this.setState({
        loggerList: [],
        log_offset: 0,
        runState: 1,
        rsHasShow: [],
      })
      // 运行前保存一下
      save(
        {
          id: this.props.fatcherType === 1 ? null : this.props.tabKey,
          taskId: this.props.taskId,
          script: this.editor.getValue(),
          params: valArr,
        },
        () => {
          run(
            {
              taskNodeId: this.props.tabKey,
              taskId: this.props.taskId,
              script: this.editor.getSelection().isEmpty()
                ? this.editor.getValue()
                : this.editor.getModel().getValueInRange(this.editor.getSelection()),
              params: valArr,
              execType: 0,
            },
            (res) => {
              const { data, msg } = res
              const { resultss } = this.state
              this.logTimeout && clearTimeout(this.logTimeout)
              this.setState({
                jobId: data,
                runState: 1,
                activeKey: '0',
                isRunState: true,
              }, () => {
                this.fetchLog()
              })
              this.state.loggerList = [msg]
              this.state.resultss[0].value = this.state.loggerList
              this.state.resultss[0].name = '运行日志'
              this.setState({
                resultss: [...resultss],
              })
              this.runTime = setInterval(() => {
                this.state.loggerList.push('等待资源中...')
                const tempData = [...resultss]
                tempData[0].value = this.state.loggerList
                this.setState({
                  resultss: tempData,
                })
              }, 2000)
              Bus.emit('dataDevTaskFlowNodeDe', this.props.taskId)
            },
            (res) => {
              const { msg = '运行失败' } = res
              const { resultss } = this.state
              Bus.emit('dataDevTaskFlowNodeDe', this.props.taskId)
              const tempData = [...resultss]
              tempData[0].value = [msg]
              this.setState({
                resultss: tempData,
                runState: 0,
                activeKey: '0',
              })
              notification.error({
                message: '服务端错误',
                description: msg,
              })
            },
          )
        },
      )
    } else {
      notification.warning({
        message: '任务参数值不能为空',
        style: {
          paddingRight: 20,
        },
      })
    }
  };

  // 锁定/解锁脚本
  lock = () => {
    const { isLocked } = this.state
    lockTask({
      id: this.props.tabKey,
      status: isLocked ? 0 : 1,
    }).then(
      (res) => {
        this.setState({
          isLocked: !isLocked ? 1 : 0,
        }, () => {
          Bus.emit('RefreshDevTree')
          notification.success({
            message: isLocked ? '已解锁' : '已锁定',
            style: {
              paddingRight: 20,
            },
          })
        })
      },
      (error) => {

      },
    )
  }

  setTimeoutFn = () => {
    this.saveTime && clearTimeout(this.saveTime)
    this.saveTime = setTimeout(() => {
      if (this.props.hasEdited) {
        this.save('自动保存')
      }
    }, 120000)
  }

  // 保存当前脚本
  save = async (messageText) => {
    this.setState({
      isSave: false,
    }, () => {
      let valArr = null
      let bool = true
      if (this.props.curstoMvariablesArr) {
        this.props.curstoMvariablesArr.map((obj) => {
          if (this.props.tabKey === (obj.task && obj.task.id)) valArr = obj.arr
        })
      }
      valArr
        && valArr.map((obj) => {
          if (!obj.paramValue) {
            bool = false
          }
        })
      const scriptValue = this.editor.getValue()
      if (bool) {
        this.setState({ spinning: true })
        save(
          {
            id: this.props.fatcherType === 1 ? null : this.props.tabKey,
            taskId: this.props.taskId,
            script: scriptValue,
            params: valArr,
            isLocked: this.state.isLocked,
          },
          // eslint-disable-next-line consistent-return
          (res) => {
            this.setState({
              isSave: true,
            })
            if (res.status === 200) {
              const arr = JSON.parse(sessionStorage.getItem('dataDevArr'))
              Bus.emit('dataDevTaskFlowNodeDe', this.props.taskId)
              this.setState({ spinning: false })
              if (arr) {
                arr.map((obj, key) => {
                  if (this.props.tabKey === obj.id) {
                    arr.splice(key, 1)
                  }
                })
              }

              // 保存成功之后需要更新缓存，清除已编辑标记
              monacoCache[this.props.tabKey] = scriptValue;
              (this.props.editorContentChange || noop)(this.props.tabKey, false)
              sessionStorage.setItem('dataDevArr', JSON.stringify(arr))
              return notification.success({
                message: messageText || '保存当前脚本！',
                style: {
                  paddingRight: 20,
                },
              })
            }
            this.setState({ spinning: false })
            return notification.warning({
              message: `保存失败！${res.msg}`,
              style: {
                paddingRight: 20,
              },
            })
          },
        )
      } else {
        this.setState({
          isSave: true,
        })
        notification.warning({
          message: '任务参数值不能为空',
          style: {
            paddingRight: 20,
          },
        })
      }
    })
  };

  insertToCursor = (textToInsert = '') => {
    if (!this.editor) return
    const editor = this.editor
    const position = editor.getPosition()
    if (position) {
      if (!(position && position.lineNumber)) {
        position.lineNumber = 1
        position.column = 1
      }
      this.executeEdits(editor, textToInsert, position)
      position.column += textToInsert.length
      editor.setPosition(position)
    }
  };

  fetchDetail = async () => {
    const fetchDetail = (params, cb) => {
      getScript(params).then(
        (res) => {
          cb && cb(res)
        },
        (error) => {
          console.log(error)
        },
      )
    }
    const { editorTaskItem } = this.props
    const defaultValue = this.state.defaultValue[editorTaskItem.nodeType]
    fetchDetail({
      id: this.props.tabKey,
    }, (data) => {
      this.setState(
        {
          script: data && data.script,
        },
        () => {
          this.setState({
            isLocked: data && data.isLocked ? 1 : 0,
          })
        },
      )
      const taskItem = this.props.editorTaskItem
      if (data && data.params && data.params.length) {
        this.props.curstoMvariablesTask && this.props.curstoMvariablesTask(taskItem, data.params)
      }

      // 是否编辑比对的的基准是服务器存储的代码
      monacoCache[this.props.tabKey] = data && data.script
      if (!(sessionStorage.getItem('dataDevArr') === 'undefined')) {
        let val = ''
        const dataDevArr = JSON.parse(sessionStorage.getItem('dataDevArr'))
        dataDevArr && dataDevArr.length
          ? dataDevArr.map((obj) => {
            if (obj.id === this.props.tabKey) {
              val = obj.value
            }
          })
          : null
        if (val && data && data.script !== val) {
          this.setState({
            show: true,
          })
          this.child.createDiffEditor.bind(
            this,
            this.child,
            data && data.script,
            val,
          )()
        } else {
          this.setState({
            show: false,
          })
        }

        const arr = JSON.parse(sessionStorage.getItem('dataDevArr'))
        const newArr = []
        let bool = false
        arr
          && arr.map((item) => {
            item.id === this.props.tabKey
              ? (bool = item.value)
              : newArr.push(item)
          })
        if (!bool || !this.state.writeAuth) {
          newArr.push({
            id: this.props.tabKey,
            value: data && data.script,
          })
          sessionStorage.setItem('dataDevArr', JSON.stringify(newArr))
          this.editor.setValue((data && data.script) || defaultValue)
        } else {
          this.editor.setValue(bool || defaultValue)
        }
      } else {
        sessionStorage.setItem(
          'dataDevArr',
          JSON.stringify([
            {
              id: this.props.tabKey,
              value: data && data.script,
            },
          ]),
        )
        this.editor.setValue((data && data.script) || defaultValue)
      }
    })
  };

  saveEditor = (taskId) => {
    if (taskId === this.props.tabKey) {
      this.save()
    }
  };

  // 运行停止
  stop = async () => {
    if (this.state.runState === 0) return
    stopTask({
      jobId: this.state.jobId,
    }).then(
      (res) => {
        notification.success({ message: '停止成功！' })
      },
      (error) => {
        console.log(error)
      },
    )
  };

  // 运行日志和结果
  // eslint-disable-next-line react/no-unused-class-component-methods
  fetchLog = async (id) => {
    taskLog({
      jobId: id || this.state.jobId,
      offset: this.state.log_offset,
      rsHasShow: this.state.rsHasShow.join(','),
    }).then(
      (res) => {
        const {
          logEnd, offset, logContent, results,
        } = res
        const { loggerList, rsHasShow } = this.state
        if ((logContent && logContent.length) || logEnd) {
          clearInterval(this.runTime)
        }
        let newLoggerList = loggerList
        if (logContent && logContent.length) {
          newLoggerList = loggerList.concat(logContent)
        }

        this.state.resultss[0].value = newLoggerList || []

        const arr = this.state.resultss

        let tempResults
        if (results && results.length) {
          tempResults = results
          results.map((obj) => {
            obj.jobId = id || this.state.jobId
            rsHasShow.push(obj.key)
          })
          tempResults = arr.concat(results)
        } else {
          tempResults = arr
        }

        tempResults && tempResults.length
          ? tempResults.map((item, k) => {
            item.name ? null : (item.name = `结果[${k}]`)
          })
          : null
        this.setState({
          log_offset: offset,
          loggerList: newLoggerList,
          resultss: tempResults || arr,
          rsHasShow: [...rsHasShow],
        })

        if (!logEnd) {
          (this.logTimeout = setTimeout(this.fetchLog, 3000))
          return
        }
        this.setState({
          runState: 0,
          activeKey: String(results && results.length && tempResults && tempResults.length ? tempResults.length - 1 : 0),
        })

        clearTimeout(this.logTimeout)
        if (this.props.editorTaskItem.nodeType !== 14) {
          Bus.emit('refreshTableInfo')
        }
      },
      (error) => {
        clearInterval(this.runTime)
        this.setState({ runState: 0 })
      },
    )
  };

  insertQuerySql = (str) => {
    const { tabKey, editorTaskItem } = this.props
    if (editorTaskItem.id === tabKey) {
      this.insertToCursor(str)
    }
  };

  // eslint-disable-next-line class-methods-use-this
  isFullScreen() {
    return !!(
      document.fullscreen
      || document.mozFullScreen
      || document.webkitIsFullScreen
      || document.webkitFullScreen
      || document.msFullScreen
    )
  }

  showGuideChange = () => {
    this.setState({
      showGuide: false,
    })
  };

  // 配置字段
  async configSuggestion() {
    const SORT_TEXT = {
      Table: '2',
      Column: '1',
      Keyword: '0',
    }
    const db_keywords = [
      'SELECT',
      'FROM',
      'WHERE',
      'GROUP BY',
      'ORDER BY',
      'ASC',
      'DESC',
    ]

    tableList().then((data) => {
      db_schema = {
        db_keywords: data.db_keywords || [],
        table_columns: data.table_columns || [],
        tables: data.tables || [],
      }
    })

    Bus.addListener('refreshTableInfo', this.refreshTableInfo)

    window.monaco.languages.registerCompletionItemProvider('sql', {
      triggerCharacters: [' ', '.'],
      provideCompletionItems: (model, position) => {
        const { lineNumber, column } = position
        const textBeforePointer = model.getValueInRange({
          startLineNumber: lineNumber,
          startColumn: 0,
          endLineNumber: lineNumber,
          endColumn: column,
        })
        const tokens = textBeforePointer.trim().split(/\s+/)
        const lastToken = tokens[tokens.length - 1].toLowerCase()
        if (lastToken === 'from') {
          return {
            suggestions: db_schema.tables.map(renderTable),
          }
        }
        if (lastToken === 'select') {
          return {
            suggestions: getAllTableColumnCompletionItems(),
          }
        }
        return {
          suggestions: db_schema.tables
            .map(renderTable)
            .concat(getAllTableColumnCompletionItems())
            .concat(
              db_keywords.concat(db_schema.db_keywords).map(renderKeyword),
            ),
        }
      },
    })

    function getAllTableColumnCompletionItems() {
      const table_columns = []
      db_schema.table_columns.forEach((column) => {
        table_columns.push(renderTableColumn(column))
      })
      return table_columns
    }

    function renderKeyword(keyword) {
      return {
        label: keyword,
        kind: window.monaco.languages.CompletionItemKind.Keyword,
        detail: '',
        sortText: SORT_TEXT.Keyword,
        insertText: keyword,
      }
    }

    function renderTable(name) {
      return {
        label: name,
        kind: window.monaco.languages.CompletionItemKind.Module,
        detail: '<table>',
        sortText: SORT_TEXT.Table,
        insertText: name,
      }
    }

    function renderTableColumn(column) {
      return {
        label: column,
        kind: window.monaco.languages.CompletionItemKind.Field,
        detail: '',
        sortText: SORT_TEXT.Column,
        insertText: column,
      }
    }
  }

  // 自变量效验
  // eslint-disable-next-line class-methods-use-this
  independentValidity(value) {
    const strings = [
      'bdp.system.cyctime',
      'bdp.system.bizdate',
      'bdp.system.premonth',
      'bdp.system.currmonth',
    ]
    let bool = true
    strings.map((item) => {
      if (value.toLocaleLowerCase() === item) bool = false
    })
    return bool
  }

  // 关闭
  close(diffEditor) {
    this.setState({
      show: false,
    })
    diffEditor && diffEditor.dispose()
  }

  // 选择服务器内的代码
  server(data, diffEditor) {
    const { editorTaskItem } = this.props
    const defaultValue = this.state.defaultValue[editorTaskItem.nodeType]
    this.editor.setValue(data || defaultValue)
    this.setState({
      show: false,
      script: data || defaultValue,
    })
    diffEditor && diffEditor.dispose()
  }

  // 选择本地缓存的代码
  local(data, diffEditor) {
    const { editorTaskItem } = this.props
    const defaultValue = this.state.defaultValue[editorTaskItem.nodeType]
    this.editor.setValue(data || defaultValue)
    this.setState({
      show: false,
      script: data || defaultValue,
    })
    diffEditor && diffEditor.dispose()
  }

    // 查看血缘
  onConsanguinity = () => {
    this.setState({ isShowConsanguinity: true })
  }

  // 更改脚本内容到缓存
  // eslint-disable-next-line class-methods-use-this
  executeEdits(editor, changeText, position) {
    const value = editor.getValue()
    if (value !== changeText) {
      const splitedText = value.split('\n')
      const endLineNumber = splitedText.length
      const endColumn = splitedText[splitedText.length - 1].length + 1
      const range = position
        ? new window.monaco.Range(
          position.lineNumber,
          position.column,
          position.lineNumber,
          position.column,
        )
        : new window.monaco.Range(0, 0, endLineNumber, endColumn)
      const id = { major: 1, minor: 1 }
      const op = {
        identifier: id,
        range,
        text: changeText,
        forceMoveMarkers: true,
      }
      editor.executeEdits('', [op])
    }
  }

  render() {
    const {
      loggerList,
      runState,
      hide,
      resultss,
      showGuide,
      isLocked,
      writeAuth,
      isSave,
    } = this.state
    const { tabKey, taskParamValChange } = this.props
    return (
      <div className={styles.monaco} id={`monacoEditor${tabKey}`}>
        <Spin tip={this.state.spinTip} spinning={this.state.spinning}>
          {writeAuth ? (
            <div className={styles.monaco_work}>
              <dl
                onClick={() => {
                  if (isSave) {
                    this.saveTime && clearTimeout(this.saveTime)
                    this.save()
                  }
                }}
              >
                <dt>
                  <i className="iconfont icon-baocun" />
                </dt>
                <dd>保存</dd>
              </dl>
              <dl
                onClick={this.run}
                className={`${runState === 1 && styles.disable}`}
              >
                <dt>
                  <i className="iconfont icon-yunhang" />
                </dt>
                <dd>运行</dd>
              </dl>
              <dl
                onClick={this.stop}
                className={`${runState === 0 && styles.disable}`}
              >
                <dt>
                  <i className="iconfont icon-tingzhi" />
                </dt>
                <dd>停止</dd>
              </dl>
              {this.props.editorTaskItem.nodeType === 14
                || this.props.editorTaskItem.nodeType === 15
                ? null
                : [
                  <dl
                    style={{ cursor: 'pointer' }}
                    key="dl"
                    className={`${(runState === 1 || isLocked === 1) && styles.disable}`}
                  >
                    <Upload
                      showUploadList={false}
                      className={styles.upload_icon}
                      name="file"
                      data={{ nodeType: 13 }}
                      action={`${SUAN_PAN}/api/v1/offlineDev/task/script/import`}
                      headers={{ 'X-Project-Id': getProjectId() }}
                      beforeUpload={(file, fileLists) => {
                        if (file.size > 52428800) {
                          notification.warning({
                            message: '文件过大，超过50M',
                            style: {
                              paddingRight: 20,
                            },
                          })
                          return false
                        }
                        return true
                      }}
                      onChange={(info) => {
                        if (info.file.status === 'done') {
                          if (info.file.response) {
                            const { data, msg, status } = info.file.response
                            if (status === 200) {
                              message.success('导入成功！')
                              this.insertToCursor(data)
                            } else {
                              notification.warning({
                                message: `${info.file.name} 上传失败.`,
                                description: msg,
                                style: {
                                  paddingRight: 20,
                                },
                              })
                            }
                          }
                        } else if (info.file.status === 'error') {
                          message.error(`${info.file.name} 上传失败.`)
                        }
                      }}
                    >
                      <dt>
                        <i className="iconfont icon-daoru" />
                      </dt>
                      <dd>导入本地脚本</dd>
                    </Upload>
                  </dl>,
                ]}
              <dl
                onClick={() => {
                  this.lock()
                }}
              >
                <dt>
                  <i
                    className={`iconfont ${isLocked ? 'icon-suo' : 'icon-jiesuo'}`}
                  />
                </dt>
                <dd>{isLocked ? '解锁' : '锁定'}</dd>
              </dl>
              <dl
                onClick={() => {
                  const fullarea = document.getElementById(
                    `monacoEditor${tabKey}`,
                  )
                  if (this.isFullScreen()) {
                    // 退出全屏
                    if (document.exitFullscreen) {
                      document.exitFullscreen()
                    } else if (document.webkitCancelFullScreen) {
                      document.webkitCancelFullScreen()
                    } else if (document.mozCancelFullScreen) {
                      document.mozCancelFullScreen()
                    } else if (document.msExitFullscreen) {
                      document.msExitFullscreen()
                    }
                  } else {
                    // 进入全屏
                    // eslint-disable-next-line no-lonely-if
                    if (fullarea.requestFullscreen) {
                      fullarea.requestFullscreen()
                    } else if (fullarea.webkitRequestFullScreen) {
                      fullarea.webkitRequestFullScreen()
                    } else if (fullarea.mozRequestFullScreen) {
                      fullarea.mozRequestFullScreen()
                    } else if (fullarea.msRequestFullscreen) {
                      // IE11
                      fullarea.msRequestFullscreen()
                    }
                  }
                }}
              >
                <dt>
                  <i className="iconfont icon-quanping" />
                </dt>
                <dd>全屏</dd>
              </dl>
              {
                (this.props.editorTaskItem.nodeType === 13 && this.props.showImportWork) ? (
                  <dl
                    onClick={() => {
                      this.workForm.current?.resetFields()
                      this.setState({ workModalVisible: true })
                    }}
                  >
                    <dt>
                      <i className="iconfont icon-yinyong1" />
                    </dt>
                    <dd>引用作业</dd>
                  </dl>
                ) : null
              }
              <dl onClick={() => {
                this.onConsanguinity()
              }}
              >
                <dt><i className="iconfont icon-zhexiantu-xianxing" /></dt>
                <dd>血缘分析</dd>
              </dl>
            </div>
          ) : null}
          <div className={styles.monaco_container}>
            <div className={styles.monaco_container_box}>
              <div
                className={styles.monaco_container_editor}
                id={`monaco-container${tabKey}`}
                ref={(div) => {
                  this.monacoEditor = div
                }}
              />
              {[13, 14, 15, 16, 17, 18, 19].includes(
                this.props.editorTaskItem.nodeType,
              ) ? (
                <TaskParam
                  editorTaskItem={this.props.editorTaskItem}
                  taskParamValChange={taskParamValChange}
                  curstoMvariablesArrMr={this.props.curstoMvariablesArrMr}
                  curstoMvariablesArr={this.props.curstoMvariablesArr}
                />) : null}
            </div>
            <DataDevLogger
              nodeType={this.props.editorTaskItem.nodeType}
              editor={this.editor}
              list={loggerList || []}
              results={resultss}
              jobId={this.state.jobId}
              hide={hide}
              isRunState={this.state.isRunState}
              activeKey={this.state.activeKey}
              activeKeyonChange={this.activeKeyonChange}
              onEdit={this.onEdit}
              resultssChange={this.resultssChange}
              taskId={this.props.editorTaskItem.id}
            />
          </div>
          <CreateDiffEditor
            show={this.state.show}
            onRef={this.onRef.bind(this)}
            server={this.server.bind(this)}
            local={this.local.bind(this)}
            close={this.close.bind(this)}
          />
          <Modal
            visible={this.state.workModalVisible}
            title="引用作业"
            onCancel={() => this.setState({ workModalVisible: false })}
            onOk={this.onImportWorKOk}
          >
            <Form
              ref={this.workForm}
              labelCol={{ span: 5 }}
              wrapperCol={{ span: 19 }}
            >
              <Form.Item
                label="选择作业"
                rules={[
                  { required: true, message: '请选择作业' },
                ]}
                name="workId"
              >
                <TreeSelect
                  treeData={getNavTreeDataFunc(this.state.workTreeOptions) || []}
                  placeholder="请选择作业"
                  filterTreeNode={(input, treeNode) => treeNode.name.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  showSearch
                  treeDefaultExpandAll
                />
              </Form.Item>
            </Form>
          </Modal>
        </Spin>
        {showGuide && (
          <Guide
            nodeType={this.props.editorTaskItem.nodeType}
            showGuideChange={this.showGuideChange}
          />
        )}
        <ConsanguinityModal visible={this.state.isShowConsanguinity} onClose={() => { this.setState({ isShowConsanguinity: false }) }} obj={{ nodeId: this.props.editorTaskItem.id }} />
      </div>
    )
  }
}
